/**
 * @Title: BaseWebSocketServer.java
 * @Package com.sl.module.websocket.face
 * @author jiang
 * @date 2018年9月4日 上午11:29:17
 * @version V1.0
 */
package com.dawn.websocket.server.server;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

import javax.websocket.OnClose;
import javax.websocket.OnError;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author jiang
 * @ClassName: BaseWebSocketServer
 * @date 2018年9月4日 上午11:29:17
 */
public abstract class BaseWebSocketServer implements WebSocketServerFace {

    protected static Logger logger = LoggerFactory.getLogger(BaseWebSocketServer.class);

    // 客户端标识前缀
    private final static String NAME_PREFIX = "NAME_";

    // 数量
    protected static volatile int onlineCount = 0;

    // 全部客户端，当作游客
    // concurrent包的线程安全Set，用来存放每个客户端对应的MyWebSocket对象。若要实现服务端与单一客户端通信的话，可以使用Map来存放，其中Key可以为用户标识
    protected static CopyOnWriteArraySet<BaseWebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();

    // 有标识的session，当作可信客户端
    protected static ConcurrentHashMap<String, Set<BaseWebSocketServer>> webSocketMap = new ConcurrentHashMap<>();

    // 与某个客户端的连接会话，需要通过它来给客户端发送数据
    protected Session session;

    /**
     * 连接建立成功调用的方法
     *
     * @param session 可选的参数。session为与某个客户端的连接会话，需要通过它来给客户端发送数据
     */
    @OnOpen
    public void onOpen(Session session) {
        this.session = session;
        webSocketSet.add(this); // 加入set中
        addOnlineCount(); // 在线数加1
        logger.debug("有新连接加入！当前在线人数为" + getOnlineCount());
    }

    /**
     * 连接关闭调用的方法
     */
    @OnClose
    public void onClose() {
        webSocketSet.remove(this); // 从set中删除
        subOnlineCount(); // 在线数减1
        logger.debug("有一连接关闭！当前在线人数为" + getOnlineCount());
    }

    /**
     * 收到客户端消息后调用的方法
     *
     * @param message 客户端发送过来的消息
     * @param session 可选的参数
     */
    @OnMessage
    public void onMessage(String message, Session session) {
        logger.debug("来自客户端的消息:" + message);
        if (message == null || "".equals(message)) {
            return;
        }
        // 添加带标识的客户端
        if (message.startsWith(NAME_PREFIX)) {
            // 添加可信客户端，未从游客列表剔除
            String name = message.substring(NAME_PREFIX.length());
            if (webSocketMap.get(name) == null) {
                webSocketMap.put(name, new CopyOnWriteArraySet<>());
            }
            webSocketMap.get(name).add(this);
            return;
        }
        dealMessage(message);
    }

    /**
     * 发生错误时调用
     *
     * @param session
     * @param error
     */
    @OnError
    public void onError(Session session, Throwable error) {
        logger.error("err", error);
    }

    /**
     * 这个方法与上面几个方法不一样。没有用注解，是根据自己需要添加的方法。
     *
     * @param message
     * @throws IOException
     */
    public void sendMessage(String message) throws IOException {
        synchronized (this.session) {
            this.session.getBasicRemote().sendText(message);
            // this.session.getAsyncRemote().sendText(message);
        }
    }

    public static int sendMessage4all(String message) {
        int num = 0;
        for (BaseWebSocketServer item : webSocketSet) {
            try {
                item.sendMessage(message);
            } catch (Exception e) {
                webSocketSet.remove(item);
                subOnlineCount(); // 在线数减1
                logger.error("err", e);
            }
            ++num;
        }
        return num;
    }

    public static int sendMessage4name(String name, String message) {
        int num = 0;
        Set<BaseWebSocketServer> set = webSocketMap.get(name);
        if (set == null) {
            return num;
        }
        for (BaseWebSocketServer item : set) {
            try {
                item.sendMessage(message);
                ++num;
            } catch (Exception e) {
                webSocketMap.get(name).remove(item);
                if (webSocketMap.get(name).size() == 0) {
                    webSocketMap.remove(name);
                }
                webSocketSet.remove(item);
                subOnlineCount(); // 在线数减1
                logger.error("err", e);
            }
        }
        return num;
    }

    public static int getOnlineCount() {
        return onlineCount;
    }

    public static synchronized void addOnlineCount() {
        BaseWebSocketServer.onlineCount++;
    }

    public static synchronized void subOnlineCount() {
        BaseWebSocketServer.onlineCount--;
    }

    public static List<String> getClientNames() {
        List<String> keyList = new ArrayList<>();
        Enumeration<String> enumeration = webSocketMap.keys();
        while (enumeration.hasMoreElements()) {
            keyList.add(enumeration.nextElement());
        }
        return keyList;
    }
}
